// ingressModel.go
package models

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"myk8s/common"
	"strings"

	"github.com/tidwall/gjson"

	corev1 "k8s.io/api/core/v1"
	networkingv1 "k8s.io/api/networking/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	yamlutil "k8s.io/apimachinery/pkg/util/yaml"
	"sigs.k8s.io/yaml"
)

type Ingress struct {
	IngressName  string `json:"ingressName"`
	NameSpace    string `json:"nameSpace"`
	Labels       string `json:"labels"`
	IngressClass string `json:"ingressClass"`
	Rules        string `json:"rules"`
	Endpoint     string `json:"endpoint"` //内部端点
	CreateTime   string `json:"createTime"`
}

type KV struct {
	Key   string `json:"key"`
	Value string `json:"value"`
}

func IngList(kubeconfig, namespace, ingressName, serviceName string, labelsKey, labelsValue string) ([]Ingress, error) {
	clientset := common.ClientSet(kubeconfig)
	if namespace == "" {
		//namespace = corev1.NamespaceDefault
		namespace = corev1.NamespaceAll
	}

	var listOptions = metav1.ListOptions{}
	if labelsKey != "" && labelsValue != "" {
		listOptions = metav1.ListOptions{LabelSelector: fmt.Sprintf("%s=%s", labelsKey, labelsValue)}
	}

	ingList, err := clientset.NetworkingV1().Ingresses(namespace).List(context.TODO(), listOptions)
	if err != nil {
		log.Printf("list service error:%v\n", err)
	}

	var bbb = make([]Ingress, 0)
	for _, ing := range ingList.Items {
		//搜索
		if ingressName != "" {
			if !strings.Contains(ing.Name, ingressName) {
				continue
			}
		}

		var labelsStr string
		for kk, vv := range ing.ObjectMeta.Labels {
			labelsStr += fmt.Sprintf("%s:%s,", kk, vv)
		}
		if len(labelsStr) > 0 {
			labelsStr = labelsStr[0 : len(labelsStr)-1]
		}
		var rulestr string
		var isNeedService bool
		for _, v1 := range ing.Spec.Rules {
			for _, v2 := range v1.HTTP.Paths {
				var portstr string
				if v2.Backend.Service.Port.Number > 0 {
					portstr = fmt.Sprintf(":%d", v2.Backend.Service.Port.Number)
				}
				rulestr += fmt.Sprintf("%s%s-->%s%s,", v1.Host, v2.Path, v2.Backend.Service.Name, portstr)

				//用serviceName来搜索
				if serviceName != "" && v2.Backend.Service.Name == serviceName {
					isNeedService = true
				}
			}
		}
		//用serviceName来搜索
		if serviceName != "" && !isNeedService {
			continue
		}

		var endpointIp string
		if len(ing.Status.LoadBalancer.Ingress) > 0 {
			endpointIp = ing.Status.LoadBalancer.Ingress[0].IP
		}

		Items := &Ingress{
			IngressName:  ing.Name,
			NameSpace:    ing.Namespace,
			IngressClass: ing.ObjectMeta.Annotations["kubernetes.io/ingress.class"],
			Rules:        rulestr,
			Endpoint:     endpointIp,
			Labels:       labelsStr,
			CreateTime:   ing.CreationTimestamp.Format("2006-01-02 15:04:05"),
		}
		bbb = append(bbb, *Items)
	}
	return bbb, err
}

func IngCreate(kubeconfig string, bodys []byte) error {
	gp := gjson.ParseBytes(bodys)
	clusterId := gp.Get("clusterId").String()
	if kubeconfig == "" {
		kubeconfig = clusterId
	}
	ingressName := gp.Get("ingressName").String()
	nameSpace := gp.Get("nameSpace").String()
	ingressHost := gp.Get("ingressHost").String()

	var labelsMap = make(map[string]string)
	labelsMap["app"] = ingressName
	for _, vv := range gp.Get("lables").Array() {
		labelsMap[vv.Get("key").Str] = vv.Get("value").Str
	}

	var annotationsMap = make(map[string]string)
	annotationsMap["kubernetes.io/ingress.class"] = "nginx"

	var ingresPaths []networkingv1.HTTPIngressPath
	paths := gp.Get("paths").Array()
	for _, vv := range paths {
		var pathType networkingv1.PathType
		switch vv.Get("pathType").Str {
		case "ImplementationSpecific":
			pathType = networkingv1.PathTypeImplementationSpecific
		case "Exact":
			pathType = networkingv1.PathTypeExact
		default:
			pathType = networkingv1.PathTypePrefix
		}

		ingresPaths = append(ingresPaths, *&networkingv1.HTTPIngressPath{
			Path:     vv.Get("path").Str,
			PathType: &pathType,
			Backend: networkingv1.IngressBackend{
				Service: &networkingv1.IngressServiceBackend{
					Name: vv.Get("serviceName").Str,
					Port: networkingv1.ServiceBackendPort{
						Number: int32(vv.Get("servicePort").Int()),
					},
				},
			},
		})
	}

	newIngress := &networkingv1.Ingress{
		ObjectMeta: metav1.ObjectMeta{
			Name:        ingressName,
			Namespace:   nameSpace,
			Labels:      labelsMap,
			Annotations: annotationsMap,
		},
		Spec: networkingv1.IngressSpec{
			Rules: []networkingv1.IngressRule{
				{
					Host: ingressHost,
					IngressRuleValue: networkingv1.IngressRuleValue{
						HTTP: &networkingv1.HTTPIngressRuleValue{
							Paths: ingresPaths,
						},
					},
				},
			},
		},
	}

	tlsCert := gp.Get("tlsCert").String()
	if tlsCert != "" {
		var ingTls = []networkingv1.IngressTLS{
			{
				Hosts:      []string{ingressHost},
				SecretName: tlsCert,
			},
		}
		newIngress.Spec.TLS = ingTls
	}

	// 创建Service
	clientset := common.ClientSet(kubeconfig)
	_, err := clientset.NetworkingV1().Ingresses(nameSpace).Create(context.TODO(), newIngress, metav1.CreateOptions{})
	return err
}

func IngYamlModify(kubeconfig string, yamlData []byte) error {
	data, err := yamlutil.ToJSON(yamlData)
	if err != nil {
		return err
	}
	ingress := &networkingv1.Ingress{}
	err = json.Unmarshal(data, ingress)
	if err != nil {
		return err
	}

	namespace := ingress.ObjectMeta.Namespace
	ingressName := ingress.ObjectMeta.Name
	clientset := common.ClientSet(kubeconfig)
	_, err = clientset.NetworkingV1().Ingresses(namespace).Update(context.TODO(), ingress, metav1.UpdateOptions{})
	if err != nil {
		return err
	}
	fmt.Println(namespace, ingressName)
	return err
}

func GetIngYaml(kubeconfig, namespace, ingressName string) (string, error) {
	ingClient := common.ClientSet(kubeconfig).NetworkingV1().Ingresses(namespace)
	ingress, err := ingClient.Get(context.TODO(), ingressName, metav1.GetOptions{})
	ingresseUnstructured, err := runtime.DefaultUnstructuredConverter.ToUnstructured(ingress)
	if err != nil {
		return "", err
	}
	yamlBytes, err := yaml.Marshal(ingresseUnstructured)
	if err != nil {
		return "", err
	}
	return string(yamlBytes), nil
}
